package com.cheetah.support.mongo;

import com.cheetah.config.Mongo;
import com.cheetah.config.MongoConfig;
import com.cheetah.support.ConfigHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.Yaml;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 动态注册mongoTemplate实例配置
 * 后续不同实例库的template可以直接使用spring方式自动注入
 */
@Component
@Slf4j
@Order(Ordered.LOWEST_PRECEDENCE)
public class MongoBeanDefinitionRegistryPostProcessor
		implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, ApplicationContextAware {

	public static final String MONGO = "MONGO_";

    private Environment environment;

	private MongoConfig mongoConfig = new MongoConfig();

	private ApplicationContext applicationContext;

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
			throws BeansException {

        //定义mongo动态注入bean定义
		//构造bean定义
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
				.genericBeanDefinition(MongoTemplate.class);
		//设置依赖
		//beanDefinitionBuilder.addPropertyReference();
		BeanDefinition beanDefinition = beanDefinitionBuilder
				.getRawBeanDefinition();
		//注册bean定义
		mongoConfig.getMongoMap().forEach((k, v) -> {
			registry.registerBeanDefinition(MONGO + v.getDatabase(), beanDefinition);
		});
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
			throws BeansException {

	    //注册mongo实例，使用方式可以直接Autowired注入，由于可能注入多个实例，变量名必须使用对应实例名，并且包含MONGO_前缀
        //或者使用Qualifier指定对应bean name
		mongoConfig.getMongoMap().forEach((k,v) -> {
			BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
					.genericBeanDefinition(MongoTemplate.class, () -> {
						return MongoTemplateProcessor.getMongoTemplate(v);
					});
			BeanDefinition beanDefinition = beanDefinitionBuilder
					.getRawBeanDefinition();
			((DefaultListableBeanFactory) beanFactory)
					.registerBeanDefinition(MONGO + v.getDatabase(), beanDefinition);
		});
	}


	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
		environment.getProperty("redis.redisMap");
		environment.getProperty("redis.redisMap.IM");
		String property = environment.getProperty("spring.config.location");
		Yaml yaml = new Yaml();
		FileInputStream in = null;
		try {

			if(property == null){
				return;
			}

			in = new FileInputStream(property);
			Map map = yaml.loadAs(in,HashMap.class);
            ConfigHandler configHandler = new ConfigHandler();
            mongoConfig.setMongoMap(configHandler.handleConfig(map,"mongo.mongoMap",Mongo.class));

//            Config config = ConfigService.getAppConfig();
//			configHandler.getConfigFromApollo(config,"mongo.mongoMap",Mongo.class);
//			addApolloListener(config);
		} catch (FileNotFoundException e) {
			log.error(e.getMessage(),e);
		}finally {
			if(in != null){
				try {
					in.close();
				} catch (IOException e) {
					log.error(e.getMessage(),e);
				}
			}
		}
	}

//	private void addApolloListener(Config config) {
//		ConfigHandler configHandler = new ConfigHandler();
//		config.addChangeListener(new ConfigChangeListener() {
//			@Override
//			public void onChange(ConfigChangeEvent changeEvent) {
//				Map map = new LinkedHashMap();
//				for (String key : changeEvent.changedKeys()) {
//					ConfigChange change = changeEvent.getChange(key);
//					map.put(key, change.getNewValue());
//					log.info("Found change - key: {}, oldValue: {}, newValue: {}, changeType: {}", change.getPropertyName(), change.getOldValue(), change.getNewValue(), change.getChangeType());
//				}
//				Map<String, Mongo> _configMap = configHandler.handleConfigOnChange(mongoConfig.getMongoMap(),"mongo.mongoMap",
//						changeEvent,Mongo.class);
//
//				for(Map.Entry<String,Mongo> entry : _configMap.entrySet()){
//					MongoTemplate mongoTemplate = (MongoTemplate)applicationContext.getBean(MONGO + entry.getKey());
//					DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory();
//					BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(MongoTemplate.class,() -> {
//						return MongoTemplateProcessor.getMongoTemplate(entry.getValue());
//					});
////						defaultListableBeanFactory.removeBeanDefinition(entry.getKey());
//					defaultListableBeanFactory.destroyBean(MONGO + entry.getKey());
//					defaultListableBeanFactory.registerBeanDefinition(MONGO + entry.getKey(),beanDefinitionBuilder.getBeanDefinition());
//				}
//			}
//		});
//	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}
}